package com.smartwebx.ha.biz.assign.impl;

import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import com.google.common.collect.Range;
import com.google.common.primitives.Ints;
import com.smartwebx.ha.biz.assign.HaStationAreaManager;
import com.smartwebx.ha.biz.assign.HaStationGroupCfgManager;
import com.smartwebx.ha.dal.assign.bean.HaArea;
import com.smartwebx.ha.dal.assign.bean.HaStationArea;
import com.smartwebx.ha.dal.assign.bean.HaStationAreaExample;
import com.smartwebx.ha.dal.assign.bean.HaStationGroupCfg;
import com.smartwebx.ha.dal.assign.bean.HaStationGroupCfgExample;
import com.smartwebx.ha.dal.assign.bean.HaStationGroupCfgExample.Criteria;
import com.smartwebx.ha.dal.assign.bean.HaStationStationGroup;
import com.smartwebx.ha.dal.assign.bean.HaStationStationGroupExample;
import com.smartwebx.ha.dal.assign.dao.HaAreaMapper;
import com.smartwebx.ha.dal.assign.dao.HaStationAreaMapper;
import com.smartwebx.ha.dal.assign.dao.HaStationGroupCfgMapper;
import com.smartwebx.ha.dal.assign.dao.HaStationStationGroupMapper;
import com.smartwebx.ha.dal.usebean.SimpleStatGroup;
import com.smartwebx.ha.dal.usebean.SuperStatGroup;
import com.smartwebx.ha.dal.util.MyFunctions;

@Service
public class HaStationGroupCfgManagerImpl implements HaStationGroupCfgManager {
	@Autowired
	private HaAreaMapper haAreaDao;
	@Autowired
	private HaStationGroupCfgMapper haStationGroupCfgDao;
	@Autowired
	private HaStationStationGroupMapper haStationStationGroupDao;
	@Autowired
	private HaStationAreaMapper haStationAreaDao;
	@Autowired
	private HaStationAreaManager haStationAreaService;

	@Override
	public void updateHaStationGroupCfg(HaStationGroupCfg haStationGroupCfg,
			int areaId) {
		haStationGroupCfgDao.updateByPrimaryKeySelective(haStationGroupCfg);

		haStationAreaService.rePullAreaStatGroup();
	}

	@Override
	public void addHaStationGroupCfg(HaStationGroupCfg haStationGroupCfg,
			int areaId) {
		String newCode = this.getNewCode(haStationGroupCfg, areaId);
		haStationGroupCfg.setCode(newCode);
		haStationGroupCfg.setAreaId(areaId);

		haStationGroupCfgDao.insert(haStationGroupCfg);

		haStationAreaService.rePullAreaStatGroup();
	}

	@Override
	public void rmHaStationGroupCfg(int haStationGroupCfgId) {
		HaStationGroupCfg haStationGroupCfg = haStationGroupCfgDao
				.selectByPrimaryKey(haStationGroupCfgId);
		List<Integer> statGroupIds = Lists.newArrayList();

		statGroupIds.add(haStationGroupCfg.getId());
		Integer level = haStationGroupCfg.getLevel();
		if (level == 0) {// 如果为父级目录
			String parentCode = haStationGroupCfg.getCode();

			HaStationGroupCfgExample haStationGroupCfgExample = new HaStationGroupCfgExample();
			Criteria criteria = haStationGroupCfgExample.createCriteria();
			criteria.andParentCodeEqualTo(parentCode);

			List<HaStationGroupCfg> findChildStatGroupCfg = haStationGroupCfgDao
					.selectByExample(haStationGroupCfgExample);
			for (HaStationGroupCfg findChild : findChildStatGroupCfg) {
				statGroupIds.add(findChild.getId());
			}

			// 删除所有子项
			haStationGroupCfgDao.deleteByExample(haStationGroupCfgExample);
		}
		haStationGroupCfgDao.deleteByPrimaryKey(haStationGroupCfgId);

		// 根据 id 删除 ha_station_stationgroup 中所有 station_group_id 的存在值
		HaStationStationGroupExample haStationStationGroupExample = new HaStationStationGroupExample();
		haStationStationGroupExample.createCriteria().andStationGroupIdIn(
				statGroupIds);
		haStationStationGroupDao.deleteByExample(haStationStationGroupExample);

		haStationAreaService.rePullAreaStatGroup();
	}

	@Override
	public List<HaStationGroupCfg> getHaStationGroupCfgByAreaId(int areaId) {
		HaStationGroupCfgExample haStationGroupCfgExample = new HaStationGroupCfgExample();
		Criteria criteria = haStationGroupCfgExample.createCriteria();
		criteria.andAreaIdEqualTo(areaId);
		List<HaStationGroupCfg> result = haStationGroupCfgDao
				.selectByExample(haStationGroupCfgExample);

		Ordering<HaStationGroupCfg> byFirstLevelSort = new Ordering<HaStationGroupCfg>() {
			public int compare(HaStationGroupCfg o1, HaStationGroupCfg o2) {
				int sort1 = o1.getSort();
				int sort2 = o2.getSort();
				int compare = Ints.compare(sort1, sort2);
				return compare;
			}
		};

		Predicate<HaStationGroupCfg> filterFirstLevel = new Predicate<HaStationGroupCfg>() {
			public boolean apply(HaStationGroupCfg input) {
				return input.getLevel() == 0;
			}
		};

		ImmutableList<HaStationGroupCfg> firstLevelCfg = FluentIterable
				.from(result).filter(filterFirstLevel).toList();

		LinkedListMultimap<HaStationGroupCfg, HaStationGroupCfg> listMap = LinkedListMultimap.create();
		
		
		HaStationGroupCfg nullCfg = new HaStationGroupCfg();
		nullCfg.setSort(0);

		for (HaStationGroupCfg temp : firstLevelCfg) {
			ImmutableList<HaStationGroupCfg> child = FluentIterable
					.from(result).filter(filterByParentCode(temp.getCode()))
					.toList();
			List<HaStationGroupCfg> childCopy = null;
			if(child.isEmpty()){
				childCopy = Lists.newArrayList(nullCfg);
			}else{
				childCopy = byFirstLevelSort.sortedCopy(child);
			}
			listMap.putAll(temp, childCopy);
		}
		List<HaStationGroupCfg> resultBySort = Lists.newArrayList();
		for (HaStationGroupCfg parentCfg : listMap.keySet()) {
			resultBySort.add(parentCfg);
			List<HaStationGroupCfg> child = listMap.get(parentCfg);
			resultBySort.addAll(child);
		}
		resultBySort.remove(nullCfg);

		return resultBySort;
	}

	private Predicate<HaStationGroupCfg> filterByParentCode(final String code) {
		return new Predicate<HaStationGroupCfg>() {
			public boolean apply(HaStationGroupCfg input) {
				return Objects.equal(code, input.getParentCode());
			}
		};
	}

	/**
	 * 获得新的code code组成策略: 320000|01|01 根据用户所属行政区域id|父级Id|子级Id 如果为父级则子级位用00代替
	 * 
	 * @param leven
	 *            0 一级；1二级
	 * @return
	 */
	private String getNewCode(HaStationGroupCfg haStationGroupCfg, int areaId) {
		final Integer level = haStationGroupCfg.getLevel();
		HaArea haArea = haAreaDao.selectByPrimaryKey(areaId);
		String areaCode = haArea.getCode();

		List<String> codeByLevel = haStationGroupCfgDao
				.getAllCodeByLevelAndAreaId(level, areaId);

		List<Integer> exitsCode = Lists.transform(codeByLevel,
				new Function<String, Integer>() {
					public Integer apply(String input) {
						return Ints.tryParse(level == 0 ? StringUtils
								.substring(input, 6, 8) : StringUtils
								.substring(input, 8));
					}
				});
		List<Integer> allCodes = Lists.newArrayList(ContiguousSet.create(
				Range.closed(1, 99), DiscreteDomain.integers()));
		Iterables.removeAll(allCodes, exitsCode);
		Integer newKeyCode = Iterables.get(allCodes, 0);
		String newKeyCodeStr = newKeyCode < 9 ? "0" + newKeyCode : newKeyCode
				+ "";
		String keyCode = null;
		if (level == 0) {
			keyCode = newKeyCodeStr + "00";
		} else {
			String parentCode = haStationGroupCfg.getParentCode();
			String keyParentCode = StringUtils.substring(parentCode, 6, 8);
			keyCode = keyParentCode + newKeyCodeStr;
		}

		String code = areaCode + keyCode;

		return code;
	}

	@Override
	public List<HaStationGroupCfg> getHaStationGroupCfgFirstLevel(int areaId) {
		HaStationGroupCfgExample haStationGroupCfgExample = new HaStationGroupCfgExample();
		Criteria criteria = haStationGroupCfgExample.createCriteria();
		criteria.andAreaIdEqualTo(areaId).andLevelEqualTo(0);

		return haStationGroupCfgDao.selectByExample(haStationGroupCfgExample);
	}

	@Override
	public HaStationGroupCfg getHaStatoinGroupCfgById(int id) {
		return haStationGroupCfgDao.selectByPrimaryKey(id);
	}

	@Override
	public int checkStationGroupRepeat(String name, Integer level,
			String parentCode) {
		HaStationGroupCfgExample haStationGroupCfgExample = new HaStationGroupCfgExample();
		Criteria criteria = haStationGroupCfgExample.createCriteria();
		criteria.andNameEqualTo(name);
		criteria.andLevelEqualTo(level);
		if (level == 1) {
			criteria.andParentCodeEqualTo(parentCode);
		}
		List<HaStationGroupCfg> result = haStationGroupCfgDao
				.selectByExample(haStationGroupCfgExample);

		return result.isEmpty() ? 0 : 1;
	}

	@Override
	public List<String> getGisStatGroupTab(int areaId) {
		Function<HaStationGroupCfg, String> transtCodeName = new Function<HaStationGroupCfg, String>() {
			public String apply(HaStationGroupCfg input) {
				return input.getCode() + "-" + input.getName();
			}
		};
		return FluentIterable.from(getGisParentsByAreaId(areaId))
				.transform(transtCodeName).toList();
	}

	@Override
	public List<SuperStatGroup> getSuperStatGroupByAreaId(int areaId) {
		Multimap<HaStationGroupCfg, HaStationGroupCfg> parentChildsMap = getGisParentChildsMapByAreaId(areaId);
		Collection<HaStationGroupCfg> allGroupChilds = parentChildsMap.values();
		// 所有组Id
		List<Integer> allGroupIds = FluentIterable.from(allGroupChilds)
				.transform(MyFunctions.getIntIdFuns()).toList();

		if (allGroupIds.isEmpty()) {
			return Lists.newArrayList();
		}
		HaStationStationGroupExample haStationStationGroup = new HaStationStationGroupExample();
		haStationStationGroup.createCriteria().andStationGroupIdIn(allGroupIds);
		List<HaStationStationGroup> stationToStatGroup = haStationStationGroupDao
				.selectByExample(haStationStationGroup);

		Function<HaStationStationGroup, Integer> statIdFun = new Function<HaStationStationGroup, Integer>() {
			public Integer apply(HaStationStationGroup input) {
				return input.getStatId();
			}
		};

		Function<HaStationArea, String> stationFun = new Function<HaStationArea, String>() {
			public String apply(HaStationArea input) {
				return input.getStationId();
			}
		};

		List<Integer> allStatIds = FluentIterable.from(stationToStatGroup)
				.transform(statIdFun).toList();

		HaStationAreaExample haStationArea = new HaStationAreaExample();
		haStationArea.createCriteria().andIdIn(allStatIds);
		List<HaStationArea> allStationArea = haStationAreaDao
				.selectByExample(haStationArea);

		List<SuperStatGroup> result = Lists.newArrayList();
		for (HaStationGroupCfg parentCfg : parentChildsMap.keySet()) {
			SuperStatGroup superStatGroup = new SuperStatGroup();
			superStatGroup.setAtype(parentCfg.getName());
			superStatGroup.setAcode(parentCfg.getCode());
			List<SimpleStatGroup> simpleStatG = superStatGroup
					.getSimpleStatGList();

			int ord = 0;
			for (HaStationGroupCfg groupChild : parentChildsMap.get(parentCfg)) {
				ord++;

				Integer groupId = groupChild.getId();
				ImmutableList<Integer> statIds = FluentIterable
						.from(stationToStatGroup)
						.filter(this.filterByGroupId(groupId))
						.transform(statIdFun).toList();

				ImmutableList<String> stations = FluentIterable
						.from(allStationArea)
						.filter(this.filterByStatId(statIds))
						.transform(stationFun).toList();

				SimpleStatGroup simpleStat = new SimpleStatGroup();
				simpleStat.setAstcd(groupChild.getCode());
				simpleStat.setAstnm(groupChild.getName());
				simpleStat.setStcds(stations);
				simpleStat.setOrd(String.valueOf(ord));

				simpleStatG.add(simpleStat);
			}

			result.add(superStatGroup);
		}

		return result;
	}

	private Predicate<HaStationStationGroup> filterByGroupId(
			final Integer groupId) {
		return new Predicate<HaStationStationGroup>() {
			public boolean apply(HaStationStationGroup input) {
				return Objects.equal(input.getStationGroupId(), groupId);
			}
		};
	}

	private Predicate<HaStationArea> filterByStatId(final List<Integer> ids) {
		return new Predicate<HaStationArea>() {
			public boolean apply(HaStationArea input) {
				return ids.contains(input.getId());
			}
		};
	}

	private List<HaStationGroupCfg> getGisParentsByAreaId(int areaId) {
		HaStationGroupCfgExample haStationGroupCfgExample = new HaStationGroupCfgExample();
		haStationGroupCfgExample.setOrderByClause("sort");
		Criteria criteria = haStationGroupCfgExample.createCriteria();
		criteria.andAreaIdEqualTo(areaId).andIsGisTagEqualTo(true)
				.andLevelEqualTo(0);
		List<HaStationGroupCfg> allFirstLevelCfgByAreaId = haStationGroupCfgDao
				.selectByExample(haStationGroupCfgExample);
		return allFirstLevelCfgByAreaId;
	}

	private Multimap<HaStationGroupCfg, HaStationGroupCfg> getGisParentChildsMapByAreaId(
			int areaId) {
		List<HaStationGroupCfg> allFirstLevelCfgByAreaId = this
				.getGisParentsByAreaId(areaId);
		Multimap<HaStationGroupCfg, HaStationGroupCfg> tempResult = LinkedListMultimap
				.create();

		if (!allFirstLevelCfgByAreaId.isEmpty()) {
			ImmutableList<String> allCodes = FluentIterable
					.from(allFirstLevelCfgByAreaId)
					.transform(new Function<HaStationGroupCfg, String>() {
						public String apply(HaStationGroupCfg input) {
							return input.getCode();
						}
					}).toList();
			HaStationGroupCfgExample haStationGroupCfgExample = new HaStationGroupCfgExample();
			haStationGroupCfgExample.setOrderByClause("sort");
			haStationGroupCfgExample.createCriteria().andParentCodeIn(allCodes);

			List<HaStationGroupCfg> childsCfg = haStationGroupCfgDao
					.selectByExample(haStationGroupCfgExample);
			for (HaStationGroupCfg firstLevel : allFirstLevelCfgByAreaId) {
				String firstCode = firstLevel.getCode();
				ImmutableList<HaStationGroupCfg> childsCodeName = FluentIterable
						.from(childsCfg)
						.filter(this.filterByParentCode(firstCode)).toList();

				tempResult.putAll(firstLevel, childsCodeName);
			}
		}
		return tempResult;
	}

}